package com.artup.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicNameValuePair;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.artup.common.Constants;
import com.artup.common.OrderStatus;
import com.artup.common.PaymentStatus;
import com.artup.common.ResponseResult;
import com.artup.dao.OrderDao;
import com.artup.dao.PaymentDao;
import com.artup.pojo.Order;
import com.artup.pojo.Payment;
import com.artup.service.MesService;
import com.artup.service.WeChatService;
import com.artup.util.CommonUtils;
import com.artup.util.file.PropertiesUtils;
import com.artup.util.md5.MD5;

/**
 * 微信
 * @author hapday
 * @date 2017年7月25日 @Time 上午10:01:29
 */
@Service( value = "weChatService" )
public class WeChatServiceImpl implements WeChatService {
	private static final Logger LOGGER = LoggerFactory.getLogger(WeChatServiceImpl.class);
	
	private static Executor executor = Executor.newInstance();
	
	@Autowired
	private OrderDao orderDao;

	@Autowired
	private PaymentDao paymentDao;
	
	@Autowired
	private PdfServiceImpl pdfService;

	@Autowired
	private MesService mesService;
	
	@Override
	public ResponseResult createWeChatMobilePayOrder(String orderCode, float totalFee) {
		LOGGER.info("orderCode = {}, totalFee = {}", orderCode, totalFee);
		
		ResponseResult responseResult = new ResponseResult();
		
		if(StringUtils.isBlank(orderCode)) {
			responseResult.setStatus(Constants.ACTION_STATUS_PARAMETER_EMPTY);
			responseResult.setMessage("【订单号】不可为空！");
			
			return responseResult;
		}
		if(0.01F > totalFee) {
			responseResult.setStatus(Constants.ACTION_STATUS_PARAMETER_PRECISION);
			responseResult.setMessage("【总金额】不可小于 0.01！");
			
			return responseResult;
		}
		
		Order order = null;
		try {
			order = this.orderDao.selectOrderByCode(orderCode);
		} catch (SQLException e) {
			LOGGER.error("根据【编号】查询【订单详情】- 失败！", e);
			
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("根据【编号】查询【订单详情】- 失败！");
			
			return responseResult;
		}
		
		if(null == order) {
			responseResult.setStatus(Constants.ACTION_STATUS_RESULT_EMPTY);
			responseResult.setMessage("无此订单！");
			
			return responseResult;
		}
		
//		String requestXML = getProductParameter(orderCode, totalFee);
		order.setTotalPrice(0.01F);		// test
		String requestXML = getProductParameter(orderCode, order.getTotalPrice());
		LOGGER.info("requestXML = {}", requestXML);
		
		String resultXML = null;
		try {
			resultXML = executor.execute(Request.Post(PropertiesUtils.getValue("wechat.pay.api.unifiedorder"))
					.useExpectContinue()
					.bodyString(requestXML, ContentType.APPLICATION_XML.withCharset("utf-8")))
					.returnContent().asString();
		} catch (IOException e) {
			LOGGER.error("创建【微信支付】之【统一下单】 - 失败！", e);

			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("创建【微信支付】之【统一下单】 - 失败！");
		}

		LOGGER.info("result = {}", resultXML);
		
		if(StringUtils.isNotBlank(resultXML)) {
			Map<String, Object> resultMap = null;
			try {
				resultMap = this.parseXml(resultXML);
			} catch (Exception e) {
				LOGGER.error("解析【微信支付】之【创建订单结果 XML】- 失败！", e);
				
				responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
				responseResult.setMessage("解析【微信支付】之【创建订单结果 XML】- 失败！");
				
				return responseResult;
			}
			
			if(CommonUtils.isEmpty(resultMap)) {
				responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
				responseResult.setMessage("【微信支付】之【创建订单】结果为空！");
				
				return responseResult;
			}
			
			long timestamp = System.currentTimeMillis() / 1000;
			resultMap.put("package", "Sign=WXPay");
			resultMap.put("timestamp", timestamp);
			String secondSignature = this.secondSignature(resultMap);
			resultMap.put("secondSignature", secondSignature);
			
			responseResult.setData(resultMap);
		}
		
		responseResult.setStatus(Constants.ACTION_STATUS_SUCCESS);
		responseResult.setMessage("创建【微信支付】之【统一下单】 - 成功。");
		
		return responseResult;
	}

	@Override
	public ResponseResult weChatMobilePayCallback(Map<String, Object> payResultMap) {
		LOGGER.info("payNoticeMap = {}", JSONObject.toJSONString(payResultMap));
		
		String returnCode = (String) payResultMap.get("return_code");
		String resultCode = (String) payResultMap.get("result_code");
		
		ResponseResult responseResult = new ResponseResult();
		if("SUCCESS".equals(returnCode)){
			responseResult.setStatus(Constants.ACTION_STATUS_NET_SUCCESS);
			responseResult.setMessage("【微信支付二次验证】网络畅通。");

			if("SUCCESS".equals(resultCode)){
				responseResult.setStatus(Constants.ACTION_STATUS_PAY_SUCCESS);
				responseResult.setMessage("【微信支付二次验证】支付成功。");
				
				String out_trade_no = (String) payResultMap.get("out_trade_no");
				String transaction_id = (String) payResultMap.get("transaction_id");
				String total_fee = (String) payResultMap.get("total_fee");
				
				Order order = new Order();
				order.setCode(out_trade_no);
				order.setStatus(OrderStatus.getValue(OrderStatus.PAID));
				order.setTransactionCode(transaction_id);
				order.setModifierId(0L);
				
				try {
					this.orderDao.updateOrderByCode(order);

					responseResult.setStatus(Constants.ACTION_STATUS_SUCCESS);
					responseResult.setMessage("【微信支付】成功。");
					LOGGER.info("【微信支付】成功。");
				} catch (Exception e) {
					LOGGER.error("【微信支付】失败！异常：", e);

					responseResult.setStatus(Constants.ACTION_STATUS_PAY_FAILURE);
					responseResult.setMessage("【微信支付】失败！");
				}
				
				Payment payment = new Payment();
				payment.setOrderCode(out_trade_no);
				payment.setType("wechat");
				payment.setStatus(PaymentStatus.getValue(PaymentStatus.PAID));
				if(StringUtils.isBlank(total_fee)) {
					payment.setTotalPrice(0F);
				} else {
					payment.setTotalPrice(Float.parseFloat(total_fee) / 100);
				}
				
				try {
					this.paymentDao.updatePaymentByOrderCode(payment);
				} catch (SQLException e) {
					LOGGER.error("根据【订单编号】更新【支付流水】 - 失败！", e);
					
					responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
					responseResult.setMessage("根据【订单编号】更新【支付流水】 - 失败！");
					
					return responseResult;
				}
				
				this.pdfService.createPdf(out_trade_no);
				
				this.mesService.noticeMesOrder(out_trade_no);
			} else {
				responseResult.setStatus(Constants.ACTION_STATUS_PAY_FAILURE);
				responseResult.setMessage("【微信支付二次验证】支付失败。");
			}
		} else {
			responseResult.setStatus(Constants.ACTION_STATUS_NET_TIMEOUT);
			responseResult.setMessage("【微信支付二次验证】网络超时。");
		}
		
		return responseResult;
	}

	/**
	 * 获取【商品】参数
	 * @return
	 */
	private String getProductParameter(String orderCode, float totalFee) {
	    List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
	    packageParams.add(new BasicNameValuePair("appid", PropertiesUtils.getValue("wechat.pay.appId")));
	    packageParams.add(new BasicNameValuePair("body", "微信支付测试"));
	    packageParams.add(new BasicNameValuePair("mch_id", PropertiesUtils.getValue("wechat.pay.merchantCode")));
	    packageParams.add(new BasicNameValuePair("nonce_str", this.genNonceStr()));
	    packageParams.add(new BasicNameValuePair("notify_url", PropertiesUtils.getValue("wechat.pay.api.callback")));
	    packageParams.add(new BasicNameValuePair("out_trade_no", orderCode));
	    packageParams.add(new BasicNameValuePair("spbill_create_ip", PropertiesUtils.getValue("wechat.pay.callback.ip")));		// 用户端实际ip
	    packageParams.add(new BasicNameValuePair("total_fee", String.valueOf( (long) (totalFee * 100) ))); 	// 支付总金额
	    packageParams.add(new BasicNameValuePair("trade_type", "APP"));		// 交易类型
	    
	    LOGGER.info("签名前，商品参数 = " + packageParams.toString());
	    
	    String sign = signatureGenerator(packageParams);
	    packageParams.add(new BasicNameValuePair("sign", sign));
	    
	    String xmlString = toXml(packageParams);
	    
	    return xmlString;
	}
	
	/**
	 * 生成随机数
	 * @return
	 */
	private String genNonceStr() {
	    Random random = new Random();
	    return MD5.getMessageDigest(String.valueOf(random.nextInt(10000))
	            .getBytes());
	}
	
	/**
	 * 签名
	 * @param params
	 * @return
	 */
	private String signatureGenerator(List<NameValuePair> params) {
	    StringBuilder parameters = new StringBuilder();

	    for (int index = 0; index < params.size(); index++) {
	        parameters.append(params.get(index).getName());
	        parameters.append('=');
	        parameters.append(params.get(index).getValue());
	        parameters.append('&');
	    }
	    parameters.append("key=");
	    parameters.append(PropertiesUtils.getValue("wechat.pay.signature.key"));

	    String signature = MD5.getMessageDigest(parameters.toString().getBytes())
	            .toUpperCase();
	    
	    LOGGER.info("签名：{}", signature);
	    
	    return signature;
	}

	/**
	 * 二次签名
	 * @param parameterMap
	 * @return
	 */
	private String secondSignature(Map<String, Object> parameterMap) {
		StringBuilder parameters = new StringBuilder();
		
		parameters.append("appid=" + parameterMap.get("appid") + "&");
		parameters.append("noncestr=" + parameterMap.get("nonce_str") + "&");
		parameters.append("package=" + parameterMap.get("package") + "&");
		parameters.append("partnerid=" + parameterMap.get("mch_id") + "&");
		parameters.append("prepayid=" + parameterMap.get("prepay_id") + "&");
		parameters.append("timestamp=" + parameterMap.get("timestamp") + "&");
		parameters.append("key=" + PropertiesUtils.getValue("wechat.pay.signature.key"));
		
//		LOGGER.info("parameters = " + parameters);
		
		String signature = MD5.getMessageDigest(parameters.toString().getBytes())
				.toUpperCase();
		
		LOGGER.info("二次签名：{}", signature);
		
		return signature;
	}
	
	/**
	 * 转换为 XML 格式
	 * @param params
	 * @return
	 */
	private String toXml(List<NameValuePair> params) {
	    StringBuilder requestXML = new StringBuilder();
	    
	    requestXML.append("<xml>");
	    for (int i = 0; i < params.size(); i++) {
	        requestXML.append("<" + params.get(i).getName() + ">");
	        requestXML.append(params.get(i).getValue());
	        requestXML.append("</" + params.get(i).getName() + ">");
	    }
	    requestXML.append("</xml>");

	    LOGGER.info("请求参数：{}", requestXML.toString());
	    
	    return requestXML.toString();
	}
	
	/**
	 * 解析 XML
	 * @param sourceXML 原 XML 字符串
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private Map<String, Object> parseXml(String sourceXML)
			throws Exception {
		if(StringUtils.isBlank(sourceXML)) {
			return null;
		}
		
		// 读取输入流
		SAXReader reader = new SAXReader();
		Document document = reader.read(new ByteArrayInputStream(sourceXML
			     .getBytes("UTF-8")));
		
		// 得到xml根元素
		Element root = null;
		if(null != document){
			root = document.getRootElement();
		}
		
		// 得到根元素的所有子节点
		List<Element> elementList = null;
		if(null != root){
			elementList = root.elements();
		}

		Map<String, Object> resultMap = new HashMap<String, Object>();
		// 遍历所有子节点
		for (Element element : elementList){
			resultMap.put(element.getName(), element.getText());
		}
		
		return resultMap;
	}
	
	@Override
	public ResponseResult queryWeChatMobilePayOrder(String orderCode, String weChatOrderCode) {
		LOGGER.info("orderCode = {}, weChatOrderCode = {}", orderCode, weChatOrderCode);
		
		ResponseResult responseResult = new ResponseResult();
		
		if(StringUtils.isBlank(orderCode)) {
			responseResult.setStatus(Constants.ACTION_STATUS_PARAMETER_EMPTY);
			responseResult.setMessage("【订单号】不可为空！");
			
			return responseResult;
		}
		if(StringUtils.isBlank(weChatOrderCode)) {
			responseResult.setStatus(Constants.ACTION_STATUS_PARAMETER_EMPTY);
			responseResult.setMessage("【微信订单号】不可为空！");
			
			return responseResult;
		}
		
		List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
	    packageParams.add(new BasicNameValuePair("appid", PropertiesUtils.getValue("wechat.pay.appId")));
	    packageParams.add(new BasicNameValuePair("mch_id", PropertiesUtils.getValue("wechat.pay.merchantCode")));
	    packageParams.add(new BasicNameValuePair("nonce_str", this.genNonceStr()));
	    if(StringUtils.isNotBlank(orderCode)) {
	    	packageParams.add(new BasicNameValuePair("out_trade_no", orderCode));
	    }
	    if(StringUtils.isNotBlank(weChatOrderCode)) {
	    	packageParams.add(new BasicNameValuePair("transaction_id", weChatOrderCode));
	    }
	    
	    LOGGER.info("签名前，参数 = " + packageParams.toString());
	    
	    String sign = signatureGenerator(packageParams);
	    packageParams.add(new BasicNameValuePair("sign", sign));
	    
	    String requestXML = toXml(packageParams);
	    String resultXML = null;
	    
	    try {
			resultXML = executor.execute(Request.Post(PropertiesUtils.getValue("wechat.pay.api.orderquery"))
					.useExpectContinue()
					.bodyString(requestXML, ContentType.APPLICATION_XML.withCharset("utf-8")))
					.returnContent().asString();
		} catch (IOException e) {
			LOGGER.error("查询【微信移动支付订单】 - 失败！", e);

			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("查询【微信移动支付订单】  - 失败！");
			
			return responseResult;
		}

	    if(StringUtils.isBlank(resultXML)) {
	    	responseResult.setStatus(Constants.ACTION_STATUS_RESULT_EMPTY);
	    	responseResult.setMessage("查询【微信移动支付订单】结果为空！");
	    	
	    	return responseResult;
	    }
		
		LOGGER.info("result = {}", resultXML);
		
		Map<String, Object> resultMap = null;
		try {
			resultMap = this.parseXml(resultXML);
		} catch (Exception e) {
			LOGGER.error("解析【微信移动支付】之查询【微信移动支付订单】 - 失败！", e);
			
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("解析【微信移动支付】之查询【订单】- 失败！");
			
			return responseResult;
		}
		
		/*if(CommonUtils.isEmpty(resultMap)) {
			responseResult.setStatus(Constants.ACTION_STATUS_RESULT_EMPTY);
			responseResult.setMessage("【微信移动支付】之查询【订单】结果为空！");
			
			return responseResult;
		}*/
		
		if(!"SUCCESS".equals(resultMap.get("return_code"))){
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("查询【微信移动支付】订单网络异常！");
			responseResult.setData(resultMap.get("return_msg"));
			
			return responseResult;
		}
		if(!"SUCCESS".equals(resultMap.get("result_code"))){
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("查询【微信移动支付】订单结果错误！");
			responseResult.setData(resultMap.get("err_code_des"));
			
			return responseResult;
		}
		if(!"SUCCESS".equals(resultMap.get("trade_state"))){
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setData(resultMap.get("trade_state_desc"));
			
			if("REFUND".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“转入退款”！");
				
				return responseResult;
			} else if("NOTPAY".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“未支付”！");
				
				return responseResult;
			} else if("CLOSED".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“已关闭”！");
				
				return responseResult;
			} else if("REVOKED".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“已撤销”！");
				
				return responseResult;
			} else if("USERPAYING".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“支付中”！");
				
				return responseResult;
			} else if("PAYERROR".equals(resultMap.get("trade_state"))){
				responseResult.setMessage("此订单已“支付失败”！");
				
				return responseResult;
			} else {
				responseResult.setMessage("查询【微信移动支付】订单结果错误！");

				return responseResult;
			}
		}
		
		byte orderStatus = 0;
		try {
			orderStatus = this.orderDao.selectOrderStatusByCode(orderCode);
		} catch (SQLException e) {
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("根据【订单号】查询【订单状态】 - 失败！");
			
			return responseResult;
		}
		
		if(!(OrderStatus.getValue(OrderStatus.PAID) == orderStatus)) {
			responseResult.setStatus(Constants.ACTION_STATUS_FAILURE);
			responseResult.setMessage("未支付成功！");
			
			return responseResult;
		}
		
		responseResult.setStatus(Constants.ACTION_STATUS_SUCCESS);
		responseResult.setMessage("查询【微信移动支付订单】 - 成功。");
		responseResult.setData("SUCCESS");
		
		return responseResult;
	}
	
	public static void main(String[] args) {
		/*String source = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wx2421b1c4370ec43b]]></appid><mch_id><![CDATA[10000100]]></mch_id><nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str><sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id><trade_type><![CDATA[APP]]></trade_type></xml>";
		try {
			System.out.println(parseXml(source));
		} catch (Exception e) {
			e.printStackTrace();
		}*/
//		System.out.println(System.currentTimeMillis() / 1000);
		float totalFee = 0.001F;
		
		if(0.01F > totalFee) {
			System.out.println(totalFee * 100);
			System.out.println((long) (totalFee * 100));
		}
	}
}
